@file:Suppress("unused", "UNCHECKED_CAST", "FunctionName")

package com.lokiy.adapter

import android.annotation.SuppressLint
import android.util.SparseArray
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.core.util.valueIterator
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver
import androidx.viewbinding.ViewBinding
import java.lang.reflect.Method
import java.lang.reflect.ParameterizedType
import java.util.concurrent.atomic.AtomicInteger

/**
 *
 * @author Lokiy
 * @date 2023/1/8 13:30
 */

/**
 * ViewBindingAdapter
 */
abstract class ViewBindingAdapter<T, Binding : ViewBinding, Holder : BindingHolder<Binding>> : RecyclerView.Adapter<Holder>() {

    protected val data: MutableList<T> = mutableListOf()
    val list: List<T>
        get() = data.toMutableList()

    var emptyView: ViewBinding? = null//空布局
    private var recyclerView: RecyclerView? = null

    /**
     * key -> viewType, value -> ViewBinding.class
     */
    protected val typeArray: SparseArray<Class<out ViewBinding>> = SparseArray()
    private val increment = AtomicInteger(100)

    //监听数据变化
    private val adapterDataObserver = object : AdapterDataObserver() {
        override fun onChanged() {
            addOrRemoveEmptyItemDecoration()
        }

        override fun onItemRangeChanged(positionStart: Int, itemCount: Int) {
            super.onItemRangeChanged(positionStart, itemCount)
            addOrRemoveEmptyItemDecoration()
        }

        override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
            super.onItemRangeInserted(positionStart, itemCount)
            addOrRemoveEmptyItemDecoration()
        }

        override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
            super.onItemRangeRemoved(positionStart, itemCount)
            addOrRemoveEmptyItemDecoration()
        }

        override fun onItemRangeChanged(positionStart: Int, itemCount: Int, payload: Any?) {
            super.onItemRangeChanged(positionStart, itemCount, payload)
            addOrRemoveEmptyItemDecoration()
        }

        override fun onItemRangeMoved(fromPosition: Int, toPosition: Int, itemCount: Int) {
            super.onItemRangeMoved(fromPosition, toPosition, itemCount)
            addOrRemoveEmptyItemDecoration()
        }

    }

    override fun getItemCount(): Int = data.size

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
        val binding = typeArray.get(viewType).inflateMethod.invoke(this, LayoutInflater.from(parent.context), parent, false) as Binding
        return BindingHolder(binding) as Holder
    }

    override fun onBindViewHolder(holder: Holder, position: Int) {
        onBindViewHolder(holder, data.getOrNull(position))
    }

    abstract fun onBindViewHolder(holder: BindingHolder<Binding>, item: T?)

    override fun getItemViewType(position: Int): Int {
        val item = data[position] ?: return 0
        val isMixMode = this::class != LoadMoreAdapter::class

        val binding = if (isMixMode) {
            //支持 mix的需要重写这个方法
            onGetBindingType(item) ?: throw IllegalArgumentException("${item::class.java} can't get ItemViewBinding class, recommend to implement BindingItem in mix mode")
        } else {
            //单独一个itemType的可以直接拿到 class
            typeArray.valueIterator().takeIf { it.hasNext() }?.next()
            //从类上获取type
                ?: javaClass.actualType1 as Class<out ViewBinding>
        }
        val indexOfValue = typeArray.indexOfValue(binding)
        return if (indexOfValue > -1) {
            typeArray.keyAt(indexOfValue)
        } else {
            increment.incrementAndGet().also { typeArray.put(it, binding) }
        }
    }

    /**
     * 根据 item 获取item type
     */
    open fun onGetBindingType(item: T): Class<out ViewBinding>? = null

    open fun addAll(list: Collection<T>?) {
        list ?: return
        notifyItemRangeInserted(data.size, data.addAll(list).let { data.size })
    }

    open fun refresh(list: Collection<T>?) {
        list ?: return notifyItemRangeRemoved(0, data.size.also { data.clear() })
        val old = list.toMutableList()
        data.clear()
        data.addAll(list)
        val new = data.toMutableList()
        DiffUtil.calculateDiff(Diff(old, new)).dispatchUpdatesTo(this)
        addOrRemoveEmptyItemDecoration()
    }

    @SuppressLint("NotifyDataSetChanged")
    open fun notifyChanged(list: Collection<T>?) {
        list ?: return notifyItemRangeRemoved(0, data.size.also { data.clear() })
        data.clear()
        data.addAll(list)
        notifyDataSetChanged()
    }

    private fun addOrRemoveEmptyItemDecoration() {
        val emptyView = emptyView
        val recyclerView = recyclerView
        if (recyclerView != null && emptyView != null) {
            val isAdd = recyclerView.getTag(R.id.empty_has_item_decoration)?.castTo<Boolean>() ?: false
            var itemDecoration = recyclerView.getTag(R.id.empty_item_decoration)?.castTo<EmptyItemDecoration>()
            if (itemCount == 0) {
                //显示空布局
                if (itemDecoration == null) {
                    val emptyItemDecoration = EmptyItemDecoration(emptyView)
                    itemDecoration = emptyItemDecoration
                    recyclerView.setTag(R.id.empty_item_decoration, emptyItemDecoration)
                }
                if (isAdd.not()) {
                    //添加itemDecoration
                    recyclerView.addItemDecoration(itemDecoration)
                    recyclerView.setTag(R.id.empty_has_item_decoration, true)
                    recyclerView.postInvalidate()
                }
            } else if (itemDecoration != null && isAdd) {
                recyclerView.removeItemDecoration(itemDecoration)
                recyclerView.setTag(R.id.empty_has_item_decoration, false)
            }
        }
    }


    private inline fun <reified T> Any?.castTo(): T? {
        return if (this is T) this else null
    }

    private val Class<*>.actualType1
        get() = getActualType(1)

    private fun Class<*>.getActualType(pos: Int) = (genericSuperclass as ParameterizedType).actualTypeArguments[pos] as Class<*>

    private val Class<*>.inflateMethod: Method
        get() = getDeclaredMethod("inflate", LayoutInflater::class.java, ViewGroup::class.java, Boolean::class.java)

}

/**
 *
 */
open class BindingHolder<Binding : ViewBinding>(val binding: Binding) : RecyclerView.ViewHolder(binding.root)

/**
 *
 */
private class Diff<T>(val old: List<T>, val new: List<T>) : DiffUtil.Callback() {

    override fun getOldListSize(): Int = old.size

    override fun getNewListSize(): Int = new.size

    override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
        return old.getOrNull(oldItemPosition).hashCode() == new.getOrNull(newItemPosition).hashCode()
    }

    override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
        return old[oldItemPosition] == new[newItemPosition]
    }

}

/**
 *
 */
interface BindingItem {

    val binding: Class<out ViewBinding>
}